package com.fintech.pangu.oauth2.config;

import com.fintech.pangu.oauth2.properties.OAuth2AuthorizationServerProperties;
import com.fintech.pangu.oauth2.properties.client.OAuth2ClientProperties;
import com.google.common.base.Splitter;
import org.apache.commons.lang3.ArrayUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.oauth2.config.annotation.builders.InMemoryClientDetailsServiceBuilder;
import org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerEndpointsConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerSecurityConfigurer;
import org.springframework.security.oauth2.provider.code.JdbcAuthorizationCodeServices;
import org.springframework.security.oauth2.provider.token.TokenStore;

import javax.sql.DataSource;

/**
 * 授权服务器相关配置
 */
@Configuration
@EnableAuthorizationServer
@EnableConfigurationProperties(OAuth2AuthorizationServerProperties.class)
public class OAuth2AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {

    // 注入WebSecurityConfig配置的AuthenticationManager
    @Autowired
    @Qualifier("authenticationManagerBean")
    private AuthenticationManager authenticationManager;

    @Autowired
    private PasswordEncoder passwordEncoder;

    @Autowired
    private DataSource dataSource;


    // 授权服务器配置
    @Autowired
    private OAuth2AuthorizationServerProperties oAuth2AuthorizationServerProperties;

    // 读写混合TokenStore
    @Autowired
    private TokenStore readWriteCompositeTokenStore;


    /**
     * OAuth2客户端配置
     * @param clients
     * @throws Exception
     */
    @Override
    public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
        InMemoryClientDetailsServiceBuilder builder = clients.inMemory();

        // 从配置文件读取OAuth2 Client信息
        if(ArrayUtils.isNotEmpty(oAuth2AuthorizationServerProperties.getClients())){
            for(OAuth2ClientProperties client : oAuth2AuthorizationServerProperties.getClients()){
                builder.withClient(client.getClientId())
                        .secret(passwordEncoder.encode(client.getClientSecret()))
                        .redirectUris(Splitter.on(",").omitEmptyStrings().trimResults()
                                .splitToList(client.getRedirectUris()).toArray(new String[0]))
                        .authorizedGrantTypes("authorization_code", "refresh_token")
                        .accessTokenValiditySeconds(client.getAccessTokenValidateSeconds())
                        .refreshTokenValiditySeconds(client.getRefreshTokenValiditySeconds())
                        .scopes("userInfo")
                        .autoApprove(true);
            }
        }
    }

    /**
     * 配置 AuthorizationServerSecurity
     * @param oauthServer
     * @throws Exception
     */
    @Override
    public void configure(AuthorizationServerSecurityConfigurer oauthServer) throws Exception {
        oauthServer
            .tokenKeyAccess("permitAll()")
            .checkTokenAccess("isAuthenticated()"); // 客户端认证后，可以check_token
    }

    /**
     * 授权服务器端点配置
     * @param endpoints
     * @throws Exception
     */
    @Override
    public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
        endpoints
            .tokenStore(readWriteCompositeTokenStore)  // 读写混合的令牌存储
            .authenticationManager(authenticationManager)  // 使用WebSecurityConfig配置的AuthenticationManager
            .authorizationCodeServices(new JdbcAuthorizationCodeServices(dataSource));  //授权码 Jdbc存储，默认只有InMemory和JDBC的实现
    }

}
